---
title: Handling Issues
sidebar:
  order: 3
---

import { Tabs, TabItem } from '@astrojs/starlight/components';

A long report can be frustrating. The list may contain false positives, but also
tons of useful information. To get the most value out of Knip, it may require
some initial configuration.

This guide helps you deal with false positives to find solutions and create the
perfect report, with minimal configuration that will keep your project tidy.

If you start out using Knip in a large project and have a long report, it makes
sense to go over the issue types one by one. For instance, reducing the number
of unused files will also reduce the number of unused dependencies.

1. [Unused files][1]
2. [Unused dependencies][2]
3. [Unresolved imports][3]
4. [Unused exports][4]

## Unused files

Getting the list of unused files right trickles down into the other issue types
as well, so we start here. Files are reported as unused if they are in the set
of `project` files, but not in the set of files resolved from the `entry` files:

```
unused files = project files - (entry files + resolved files)
```

Let's go over common causes for unused files:

- [Missing generated files][5]
- [Dynamic import specifiers][6]
- [Unsupported arguments in scripts][7]
- [Unsupported file formats][8]
- [Missing plugin][9]
- [Incomplete plugin][10]
- [TypeScript path aliases in monorepos][11]
- [Relative paths across workspaces][12]
- [Integrated monorepos][13]
- [Auto-mocking or auto-imports][14]

In most cases you can add `entry` patterns manually.

Use `--files` to [filter the report][15] and focus only on unused files:

```sh
knip --files
```

This works with other issue types as well. For instance, use `--dependencies` to
focus only on dependencies and exclude issues related to unused files and
exports.

:::caution

Don't add unused files to the `ignore` option before reading [configuring
project files][16]. Learn why and when to use `entry`, `project`, production
mode and `ignore` patterns for better results and performance.

:::

### Missing generated files

For certain features, Knip needs to run after relevant files are generated. For
instance, [source mapping][17] in a monorepo may require files to be built into
`dist` folders first. And generated files in the `src` directory may import
other files. For instance, the `src/routeTree.gen.ts` file generated by
`@tanstack/router` must exist so Knip can find the imported route files.

**Solution**: compile and/or generate the relevant files first so Knip can
resolve and find the source files.

If Knip still reports false positives, you may need to strategically add an
`entry` file manually.

### Dynamic import specifiers

Dynamic import specifiers aren't resolved, such as:

```ts
const entry = await import(path.join(baseDir, 'entry.ts'));
```

**Solution**: add `entry.ts` to `entry` patterns.

### Unsupported arguments in scripts

Some tooling command arguments aren't recognized:

```json
{
  "name": "my-lib",
  "version": "1.0.0",
  "scripts": {
    "build": "unknown-build-cli --entry production.ts"
  }
}
```

**Solution**: add `production.ts` to `entry` patterns.

This works the same for any script, also those in GitHub Actions workflows or
Git hooks. See [script parser][18] for more details about Knip's script parser.

### Unsupported file formats

Entry files referenced in HTML files (e.g. `<script src="production.js">`).

```html
<html>
  <body>
    <script type="module" src="production.js"></script>
  </body>
</html>
```

**Solution**: add `production.js` to `entry` patterns. Or add an `.html`
compiler to extract and resolve the value of `<script src>` elements.

Knip has support for some popular framework formats through [compilers][19], and
additional compilers can be added for for any file type. The recommended
solution is usually to add the file as shown in each example as an `entry` file.

### Missing plugin

You might be using a tool or framework for which Knip doesn't have a plugin
(yet). Configuration and entry files (and related dependencies) may be reported
as unused because there is no plugin yet that would include those files. Two
examples:

- Configuration file `tool.config.js` contains a reference to the package
  `"@tool/plugin"` → both the file and the dependency are reported as unused.
- A framework automatically imports all files matching `src/models/*.ts` → those
  files are reported as unused.

**Solution**: [create a new plugin][20] for the tool or framework that's not [in
the list][21] yet. Or work around it and add `entry` patterns and maybe ignore a
dependency or two (using [`ignoreDependencies`][22]).

### Incomplete plugin

Files may be reported as unused if existing plugins do not include that entry
file pattern yet. See the [plugins section of entry files][23] for more details.

**Solution**: [override plugin configuration][24] to customize default patterns
for existing plugins. Or even better: send a pull request to improve the plugin.

### TypeScript path aliases in monorepos

When using TypeScript path aliases in import specifiers, aliases referencing
other workspaces are not special-cased. This may cause false positives. For
Knip, it's better to be explicit and list other workspaces as dependencies in
`package.json`.

**Solution**: move such "workspace aliases" from `compilerOptions`...

```json title="tsconfig.json"
{
  "compilerOptions": {
    "paths": {
      "@org/common/*": ["packages/common/*"]
    }
  }
}
```

...to `dependencies` or `devDependencies` in `package.json`:

```json title="package.json"
{
  "name": "@org/lib",
  "dependencies": {
    "@org/common": "workspace:*"
  }
}
```

An additional benefit is that Knip will report unused and unlisted dependencies
from now on.

Also see [FAQ: Why can't I use path aliases to reference other workspaces?][25]

### Relative paths across workspaces

Relative paths to import from other workspaces are not special-cased. For Knip,
it's better to be explicit and list other workspaces as dependencies in
`package.json` to be used in imports.

**Solution**: migrate from relative paths...

```ts
import { something } from '../../common/file.ts';
```

...to dependency-based imports...

```ts
import { something } from '@org/common';
```

An additional benefit is that Knip will report unused and unlisted dependencies
from now on.

Also see [TypeScript path aliases in monorepos][11].

### Integrated monorepos

Multiple instances of configuration files like `.eslintrc` and
`jest.config.json` across the repository may be reported as unused when working
in a (mono)repo with a single `package.json`.

**Solution**: see [integrated monorepos][26] for more details and how to
configure plugins to target those configuration files.

### Auto-mocking or auto-imports

Some frameworks have features like "auto-mocking" or "auto-imports" enabled,
such as Jest and Nuxt.

**Solution**: include such entry files by extending the `entry` file patterns.
This is recommended in most cases:

```json
{
  "entry": ["src/index.ts", "src/models/*.ts"]
}
```

Alternatively, exceptions and outliers can be excluded from the analysis using
negated `project` patterns:

```json
{
  "project": ["src/**/*.ts", "!**/__mocks__/**"]
}
```

## Unused dependencies

Dependencies imported in unused files are reported as unused dependencies.
That's why it's strongly recommended to try and remedy [unused files][1] first.
Better `entry` and `project` file coverage will solve many cases of reported
unused dependencies.

The most common causes for unused dependencies include:

- [Missing or incomplete plugins][27]
- [Unrecognized references][28]
- [Type Definition Packages][29]

Use `--dependencies` to [filter the report][15] and focus only on issues related
to dependencies:

```sh
knip --dependencies
```

:::caution[Monorepo]

In a monorepo, a dependency is unused in the root workspace's `package.json` if
it's also listed in an descendent workspace, and referenced only in the
descendent workspace.

:::

### Missing or incomplete plugin

If a plugin exists and the dependency is referenced in the configuration file,
but its custom dependency finder does not detect it, then that's a false
positive. Please open a pull request or issue to fix it.

**Solution**: adding the configuration file as an `entry` file pattern may be a
temporary stopgap that fixes your situation, but it's better to create a new
plugin or fix an existing one.

### Unrecognized reference

Sometimes a reference to a dependency is unrecognizable or unreachable to Knip,
so it's a false positive and incorrectly reported as unused.

**Solution**: add a new plugin or improve an existing one. If you don't feel
like a plugin could solve it, a last resort is to use [ignoreDependencies][22].

If a binary (or "executable") is referenced you'll want to use `ignoreBinaries`
instead. See [unlisted binaries][30].

### Type Definition Packages

#### Bundled types

Many packages come with their type definitions bundled. This means the
`package.json#types` field in the package points to an internal/local type
definition file. In this case, the separate types package is obsolete.

Knip reporting this is also useful for future regressions: if a package had a
DefinitelyTyped or similar package for its types before and later on starts
shipping those types bundled with the source code, Knip will report the obsolete
types dependency as unused.

Examples include ESLint, Webpack and React Router, rendering the
`@types/eslint`, `@types/webpack` and `@types/react-router` dependencies
obsolete respectively.

**Solution**: remove the types dependency (often `@types/...`)

#### Production types

Knip is strict in the divide between `dependencies` and `devDependencies`. Some
packages are published with one or more type packages listed in `dependencies`.
In strict production mode, even when re-exported and part of the package's
public API, Knip does not try to figure out what exactly are "production types"
and expects those in `devDependencies`.

**Solution**: list exceptions in [ignoreDependencies][22].

### Unlisted dependencies

This means that a dependency is referenced directly in source code or
configuration, but not listed in `package.json`.

An unlisted dependency is usually a transitive dependency that's imported or
referenced directly. The dependency is installed (since it's a dependency of
another dependency) and lives in `node_modules`, but it's not listed explicitly
in `package.json`.

You should not rely on transitive dependencies for various reasons, including
control, security and stability.

**Solution**: install and list the dependency in `dependencies` or
`devDependencies`.

### Unlisted binaries

Binaries are executable Node.js scripts. Some npm packages, when installed, add
one or more executable files to be used from scripts in `package.json`. Examples
include TypeScript that comes with the `tsc` binary, ESLint comes with `eslint`,
Next.js with `next`, and so on.

Knip detects such binaries in scripts and checks whether there's a package
installed that includes that binary. It looks up the `bin` field in the
`package.json` file of installed packages. If it doesn't find it, it will be
reported as an unlisted binary as there is no package that contains it.

Binaries that are installed on the OS already and thus likely not meant to be
installed from npm are not reported as unlisted (details: [list of ignored
binaries in source][31]).

#### Missing binaries

An unused dependency and an unlisted binary with the same name indicates
`node_modules` not containing the relevant package. And this might be caused by
either the way your package manager installs dependencies and binaries, or by
not running Knip from the root of the repository.

**Solution**: run Knip from the project root. If needed, [lint workspaces
individually][32].

Sometimes binaries and how they're reported can be a bit confusing. See this
example:

```json
{
  "name": "lib",
  "scripts": {
    "commitlint": "commitlint --edit"
  },
  "devDependencies": {
    "@commitlint/cli": "*"
  }
}
```

This example works fine without anything reported, as the `@commitlint/cli`
package includes the `commitlint` binary. However, some script may contain
`npx commitlint` and here Knip assumes `commitlint` is the name of the package.
This technically works, as `commitlint` is a transitive dependency of
`@commitlint/cli`.

**Solution**: use `npx @commitlint/cli`

In some cases, using `npx` in a script may result in Knip not understanding
intention without an explicit `--yes` or `--no-install` flag.

**Solution**: use `npx --yes` or `npx --no-install` so Knip will either ignore
or consider the binary and package(s) referenced, respectively.

## Unresolved imports

Knip may ignore or be unable to resolve an import specifier or dependency
references. The most common causes for unresolved imports:

- [Template strings][33]
- [Extensionless imports][28]
- [Unrecognized path aliases][34]
- [External aliased imports][35]

### Template strings

Using template strings in dynamic imports might be ignored or not handled
properly by Knip, resulting in false positives. Examples of dynamic import
template strings:

```ts
import(`./${value}.ts`);
import(`@org/name/dist/${value}.js`);
```

**Solution**: for internal source files, add the file(s) to the `entry`
patterns. For external dependencies, add the dependency to the
`ignoreDependencies` list.

### Extensionless imports

Knip does not support extensionless imports for some non-standard extensions,
such as for `.svg` files. Bundlers like Webpack may support this, but Knip does
not. Here's an example:

```ts title="App.vue"
import Component from './Component'; // → Should resolve to ./Component.vue
import ArrowIcon from '../icons/Arrow'; // → Does NOT resolve to ../icons/Arrow.svg
```

The first import is resolved properly, because `.vue` is a known extension if
the Vue plugin is enabled. The second import might not be resolved, because
`.svg` is not a known extension.

The recommendation is to always add the extension when importing such files,
similar to how standard ES Modules specifies file extensions are necessary.

### Unrecognized path aliases

Knip considers TS config path aliases and [paths configured in knip.json][36],
but not those in e.g. Webpack or Vite configurations.

**Solution**: configure [paths][36] or try relative imports. Otherwise, use
[`ignoreUnresolved`][37] as a last resort.

### External aliased imports

External libraries may use aliased imports that aren't resolved by Knip.

For instance, [unplugin-icons][10] does this to import icons from icon sets as
components. Such imports are reported as unused. Use the [`paths` configuration
option][13] to tell Knip where to find the icon types:

```json title="knip.json"
{
  "paths": {
    "~icons/*": ["node_modules/unplugin-icons/types/[framework].d.ts"]
  }
}
```

Where `[framework]` is the name of the framework you're using (see [available
types][38]).

**Solution**: try [--include-libs][39] or configure [paths][36].

## Unused exports

By default, Knip does not report unused exports of `entry` files.

The most common causes for unused exports include:

- [Namespace enumerations][40]
- [External libraries][39]

Use the `--exports` flag to [filter][15] and focus only on issues related to
exports:

```sh
knip --exports
```

Use [includeEntryExports][41] to report unused exports of entry files as well.
This can be set per workspace.

### Namespace enumerations

For exports on an imported namespace, Knip considers all exports referenced if
that namespace is used in certain patterns like enumeration. Individual exports
are then **not** reported.

**Solution**: if all exports on imported namespaces should be considered
individually, include the `nsExports` issue type to disable the heuristic.

See [namespace imports][42] to see all related patterns.

### External libraries

Are the exports consumed or imported by an external library, resulting in a
non-standard consumption of your exports? Here's an example:

<Tabs>
  <TabItem label="index.js">
    ```ts
    import loadable from '@loadable/component';

    export const DynamicApple = dynamic(() =>
      import('./components.js').then(mod => mod.Apple)
    );

    export const LoadableOrange = loadable(() => import('./components.js'), {
      resolveComponent: components => components.Orange,
    });
    ```
  </TabItem>

  <TabItem label="components.js">
    ```ts
    export const Apple = () => 'Apple';
    export const Orange = () => 'Orange';
    ```
  </TabItem>
</Tabs>

Knip understands `Apple` is used, since it's standard usage. But `Orange` is
referenced through a function of an external library. For performance reasons,
Knip does not include external type definitions by default so it won't see the
export being referenced.

**Solution**: include the type definitions of external libraries with the
[--include-libs][43] flag:

```shell
knip --include-libs
```

This comes at a performance and memory penalty, but should give better results
if you need it. This flag is implied when [classMembers][44] are included (that
feature comes with roughly the same performance penalty).

### Exclude exports from the report

To exclude false positives from the report, there are a few options:

- [Ignore exports used in file][45] for exports used internally.
- Individual exports can be [tagged using JSDoc syntax][46].
- Have the export in an entry file:
  - Add the file to the `entry` file patterns array in the configuration.
  - Move the export(s) to an entry file.
  - Add the file to the `exports` field of `package.json`
- Re-export the unused export(s) from an entry file.

### Missing unused exports?

Did you expect certain exports in the report, but are they missing? They might
be exported from an entry file. In that case, use [--include-entry-exports][41]
to make Knip also report unused exports in entry files.

The exports of non-standard extensions like `.astro`, `.mdx`, `.vue` or
`.svelte` are not available by default. See [compilers][19] for more details on
how to include them.

### Class members

Unused members of exported classes are not reported by default, here's how to
enable them:

```sh
knip --include classMembers
```

This option is also available in the Knip configuration file. Note that this
feature comes at a cost: linting will take more time and more memory.

Individual class members can be [tagged using JSDoc syntax][46].

Classes exported from entry files are ignored, and so are their members. Use
[--include-entry-exports][41] to make Knip also report members of unused exports
in entry files.

### Enum members

Unused enums and unused members of exported enums are reported by default.
Reporting such members can be disabled:

```sh
knip --exclude enumMembers
```

Individual enum members can be [tagged using JSDoc syntax][46].

Enums exported from entry files are ignored, and so are their members. Use
[--include-entry-exports][41] to make Knip also report members of unused exports
in entry files.

## Feedback or false positives?

If you believe Knip incorrectly reports something as unused (i.e. there's a
false positive), feel free to create a [minimal reproduction][47] and open an
issue on GitHub. It'll make Knip better for everyone!

[1]: #unused-files
[2]: #unused-dependencies
[3]: #unresolved-imports
[4]: #unused-exports
[5]: #missing-generated-files
[6]: #dynamic-import-specifiers
[7]: #unsupported-arguments-in-scripts
[8]: #unsupported-file-formats
[9]: #missing-plugin
[10]: #incomplete-plugin
[11]: #typescript-path-aliases-in-monorepos
[12]: #relative-paths-across-workspaces
[13]: #integrated-monorepos
[14]: #auto-mocking-or-auto-imports
[15]: ../features/rules-and-filters.md#filters
[16]: ./configuring-project-files.md
[17]: ../features/source-mapping.md
[18]: ../features/script-parser.md
[19]: ../features/compilers.md
[20]: ./writing-a-plugin.md
[21]: ../reference/plugins.md
[22]: ../reference/configuration.md#ignoredependencies
[23]: ../explanations/plugins.md#entry-files
[24]: ../explanations/entry-files.md#plugins
[25]: ../reference/faq.md#why-cant-i-use-path-aliases-to-reference-other-workspaces
[26]: ../features/integrated-monorepos.md
[27]: #missing-or-incomplete-plugin
[28]: #unrecognized-reference
[29]: #type-definition-packages
[30]: #unlisted-binaries
[31]: https://github.com/webpro-nl/knip/blob/b70958a58ea255ee7a7831e404786da807ca93d7/packages/knip/src/constants.ts#L37-L139
[32]: ../features/monorepos-and-workspaces.md#lint-a-single-workspace
[33]: #template-strings
[34]: #unrecognized-path-aliases
[35]: #external-aliased-imports
[36]: ../reference/configuration.md#paths
[37]: ../reference/configuration.md#ignoreunresolved
[38]: #build-artifacts-and-ignored-files
[39]: #external-libraries
[40]: #namespace-enumerations
[41]: ../reference/configuration.md#includeentryexports
[42]: ../guides/namespace-imports.md
[43]: ../reference/cli#--include-libs
[44]: #class-members
[45]: ../reference/configuration.md#ignoreexportsusedinfile
[46]: ../reference/jsdoc-tsdoc-tags.md
[47]: ../guides/issue-reproduction
